/**
 * @description 生成代码（生成render函数）
 * @param {*} node javascript AST
 */
export function generate(node) {
  /* 上下文对象 */
  const context = {
    /* 存储最终生成的渲染函数代码的字符串 */
    code: "",
    /* 当前代码缩进的级别，默认是0，即没有缩进 */
    currentIndent: 0,
    /**
     *  换行函数，即在代码字符串种添加 \n 换行字符
     *  另外，换行时应该保留缩进，所以还需要追加 currentIndent * 2 个空格字符
     *  */
    newline() {
      context.code += "\n" + `  `.repeat(context.currentIndent);
    },
    /* 缩进函数，让currentIndent 自增后，调用换行函数 */
    indent() {
      context.currentIndent++;
      context.newline();
    },
    /* 取消缩进，让currentIndent 自减后，调用换行函数 */
    deIndent() {
      context.currentIndent--;
      context.newline();
    },
    /* 在生成代码时，通过 push 函数完成对代码的拼接 */
    push(code) {
      context.code += code;
    },
  };

  /* 调用genNode函数，完成代码生成的工作 */
  genNode(node, context);

  /* 返回生成的 render 函数 */
  return context.code;
}

/**
 * @description 完成代码生成的工作
 * @param {*} node AST节点
 * @param {*} context 生成函数上下文对象
 */
function genNode(node, context) {
  switch (node.type) {
    case "FunctionDecl":
      /* 生成函数声明语句 */
      genFunctionDecl(node, context);
      break;
    case "ReturnStatement":
      /* 生成函数return语句 */
      genReturnStatement(node, context);
      break;
    case "CallExpression":
      /* 生成函数调用语句 */
      genCallExpression(node, context);
      break;
    case "StringLiteral":
      /* 生成字符串字面量 */
      genStringLiteral(node, context);
      break;
    case "ArrayExpression":
      /* 生成数组字面量 */
      genArrayExpression(node, context);
      break;
  }
}

/**
 * @description 生成函数声明语句
 * @param {*} node AST 节点
 * @param {*} context 生成函数上下文
 */
function genFunctionDecl(node, context) {
  /* 从上下文对象中取出工具函数 */
  const { push, indent, deIndent } = context;
  push(`function ${node.id.name} `);
  push("(");
  /* 生成函数的参数 */
  genNodeList(node.params, context);
  push(`) `);
  push("{");
  /* 换行+缩进 */
  indent();
  /* 生成函数体中的代码 */
  node.body.forEach((n) => genNode(n, context));
  /* 取消缩进 */
  deIndent();
  push("}");
}

/**
 * @description 生成多个节点类型的代码
 * @param {*} node 节点
 * @param {*} context 上下文对象
 */
function genNodeList(nodes, context) {
  const { push } = context;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    /* 生成AST节点对应的代码 */
    genNode(node, context);
    /**
     * 数组：[元素1, 元素2, 元素3...]
     * 函数参数：(元素1, 元素2, 元素3...)
     */
    if (i < nodes.length - 1) {
      push(`, `);
    }
  }
}

/**
 * @description 生成函数返回值语句
 * @param {*} node AST 节点
 * @param {*} context 生成函数上下文
 */
function genReturnStatement(node, context) {
  const { push } = context;

  push(`return `);

  genNode(node.return, context);
}
/**
 * @description 生成函数调用语句
 * @param {*} node AST 节点
 * @param {*} context 生成函数上下文
 */
function genCallExpression(node, context) {
  const { push } = context;

  const { callee, args } = node;

  push(`${callee.name}(`);
  genNodeList(args, context);
  push(")");
}
/**
 * @description 生成字符串字面量
 * @param {*} node AST 节点
 * @param {*} context 生成函数上下文
 */
function genStringLiteral(node, context) {
  const { push } = context;

  push(`'${node.value}'`);
}
/**
 * @description 生成函数字面量
 * @param {*} node AST 节点
 * @param {*} context 生成函数上下文
 */
function genArrayExpression(node, context) {
  const { push } = context;
  push("[");
  genNodeList(node.elements, context);
  push("]");
}
